#include <string>
#include "httpprot_defs.h"
#include "httpprot_request.h"


namespace httpprot
{
	std::string Request::readUntilChar( char charToBreak )
	{
		std::string s;
		while( m_requestLength < m_requestDataLength )
		{
			char c = *(m_requestData + m_requestLength);
			if( c == charToBreak )
				break;
			m_requestLength += 1;
			s.push_back( c );
		}

		return s;
	}

	bool Request::testCurChar( char charToTest )
	{
		char c = *(m_requestData + m_requestLength);
		if( c != charToTest )
			return false;
		return true;
	}

	bool Request::testNextChar( char charToTest )
	{
		if( m_requestLength + 1 >= m_requestDataLength )
			return false;
		char c = *(m_requestData + m_requestLength + 1);
		if( c != charToTest )
			return false;
		return true;
	}

	bool Request::testIsEndOfData( ) const
	{
		if( m_requestLength < m_requestDataLength )
			return false;
		return true;
	}

	void Request::skipChars( char charToSkip )
	{
		while( m_requestLength < m_requestDataLength )
		{
			if( !testCurChar( charToSkip ) )
				break;
			m_requestLength += 1;
		}
	}

	void Request::skipChar( )
	{
		if( m_requestLength < m_requestDataLength )
			m_requestLength += 1;
	}

	bool Request::splitHeader( const std::string& s, std::string& keyOut, std::string& valueOut ) const
	{
		// Header = Name: SP Value

		keyOut.clear( );
		valueOut.clear( );

		if( s.empty( ) )
			return false;

		unsigned int l = s.length( );

		size_t i = s.find( ':' );
		if( i == std::string::npos )
			return false;
		if( i + 1 >= l )
			return false;
		if( s[ i + 1 ] != SP )
			return false;

		keyOut = s.substr( 0, i );
		valueOut = s.substr( i + 2 );
		return true;
	}

	void Request::makeInvalid( ParseStatus parseStatus )
	{
		m_parseStatus = parseStatus;
		m_requestLength = 0;
		return;
	}

	void Request::parseInternal( )
	{
		// Request-Line = Method SP Request-URI SP HTTP-Version CRLF
		std::string method = readUntilChar( SP );
		if( !testCurChar( SP ) )
		{
			if( testIsEndOfData( ) )
				makeInvalid( PARSE_INCOMPLETE );
			else
				makeInvalid( PARSE_ERROR );
			return;
		}

		if( !GetMethodByString( method, m_method ) )
		{
			makeInvalid( PARSE_ERROR );
			return;
		}

		skipChar( );

		m_requestUri = readUntilChar( SP );
		if( !testCurChar( SP ) )
		{
			if( testIsEndOfData( ) )
				makeInvalid( PARSE_INCOMPLETE );
			else
				makeInvalid( PARSE_ERROR );
			return;
		}

		if( m_requestUri.empty( ) )
		{
			makeInvalid( PARSE_ERROR );
			return;
		}

		skipChar( );

		std::string httpVersion = readUntilChar( CR );
		if( !testCurChar( CR ) ||
			!testNextChar( LF ) )
		{
			if( testIsEndOfData( ) )
				makeInvalid( PARSE_INCOMPLETE );
			else
				makeInvalid( PARSE_ERROR );
			return;
		}

		if( httpVersion.empty( ) )
		{
			makeInvalid( PARSE_ERROR );
			return;
		}

		skipChar( );
		skipChar( );

		unsigned int messageLength = 0;

		// Headers = (general-header | request-header | entity-header ) CRLF
		while( true )
		{
			// Header = Name: SP Value CRLF
			std::string headerString = readUntilChar( CR );

			std::string headerKey;
			std::string headerValue;
			splitHeader( headerString, headerKey, headerValue );

			if( headerKey == "Content-Length" )
				messageLength = atoi( headerValue.c_str( ) );

			if( !testCurChar( CR ) ||
				!testNextChar( LF ) )
			{
				if( testIsEndOfData( ) )
					makeInvalid( PARSE_INCOMPLETE );
				else
					makeInvalid( PARSE_ERROR );
				return;
			}

			skipChar( );
			skipChar( );

			if( headerString.empty( ) )
				break;
		}

		// Message
		if( m_requestLength + messageLength > m_requestDataLength )
		{
			makeInvalid( PARSE_INCOMPLETE );
			return;
		}

		const char * m0 = m_requestData + m_requestLength;
		m_message.insert( m_message.end( ), m0, m0 + messageLength );
		m_requestLength += messageLength;

		m_parseStatus = PARSE_OK;
	}
};